rvPlayer = function () {

	var w;
	var PLAYER_SERVER_PORT = 9449;

	var ws;
	var watchdog;
	var configPage;
	var apilog = ""; //log available through API
	
	var restartTimer = 0;
	
	this.init = function() {
		ws = new rvWebServer(PLAYER_SERVER_PORT, onRequest);
		ws.start();
		
		watchdog = new rvWatchdog();
		watchdog.start(reloadViewer);
		
		configPage = new rvPlayerConfigPage();
		configPage.init();
		
		startTimer_CheckForUpdate();
		
		if (!$rv.debug) {
			startViewerWithDelay();
		}
	};

	this.close = function() {
		ws.stop();
	};

	var log = function(msg) {
		console.log(msg);
	};

	var logToAPi = function(msg) {
		if (apilog.length > 1000) {
			apilog = apilog.substr(-500);
		}
		apilog += "<br>" + new Date().toLocaleString() + " | " + msg;
	};

	var onRequest = function(socketId, cmd, qs, keepAlive) {
		log("Player request: cmd=" + cmd + " | qs=" + qs);
		
		try {
	        
	        if (cmd === "/ping") {
	        	var cb = ws.getUrlParam(qs, "callback");
				cb = cb ? cb + "();" : "";
				ws.writeTextResponse(socketId, cb, keepAlive, ws.CONTENT_TYPE_JAVASCRIPT);

	        } else if (cmd === "/heartbeat") {
				watchdog.poke();
	        	var cb = ws.getUrlParam(qs, "callback");
				cb = cb ? cb + "();" : "";
				ws.writeTextResponse(socketId, cb, keepAlive, ws.CONTENT_TYPE_JAVASCRIPT);
	        	
	        } else if (cmd === "/set_property") {
	    		if ("true" === (ws.getUrlParam(qs, "reboot_required"))) {
	    			log("reboot_required received");
	    			reboot();
	    		} else if ("true" === (ws.getUrlParam(qs, "restart_required"))) {
	    			log("restart_required received");
	    			restart();
	    		} else if ("true" === (ws.getUrlParam(qs, "update_required"))) {
	    			log("update_required received");
	    			checkForUpdate();
	    			//Utils.restart(); 
	    		} else	if ("true" === (ws.getUrlParam(qs, "reboot_enabled"))) {
	    			var rebootTime = ws.getUrlParam(qs, "reboot_time");
	    			if (rebootTime) {
		    			setRestartTime(rebootTime); //time to restart (not reboot)
	    			} else {
	    				console.warn("reboot_time param is missing (reboot_enabled=true)");
	    			}
	    		}
				ws.writeTextResponse(socketId, "", keepAlive, ws.CONTENT_TYPE_TEXT_PLAIN);
	        } else if (cmd === "/save_property") {
	            
	    		var displayId = ws.getUrlParam(qs,"display_id");
	    		var claimId = ws.getUrlParam(qs,"claim_id");
	    		var server = ws.getUrlParam(qs,"server");
	    		var serverUrl = ws.getUrlParam(qs,"server_url");
	    		var restartViewerParam = ws.getUrlParam(qs,"restart_viewer");
	    		
	    		$rv.config.displayId = (displayId != null) ? displayId : $rv.config.displayId;
    			$rv.config.claimId = (claimId != null) ? claimId : $rv.config.claimId;
    			$rv.config.server = (server != null) ? server : $rv.config.server;
    			$rv.config.serverUrl = (serverUrl != null) ? decodeURIComponent(serverUrl) : $rv.config.serverUrl;

	    		$rv.config.saveDisplayProperties(function() {
		    		if ("true" === restartViewerParam) {
		    			restartViewer();
		    		}
	    		});
				ws.writeTextResponse(socketId, "", keepAlive, ws.CONTENT_TYPE_TEXT_PLAIN);
	    		
	        } else if (cmd === "/clear") {
	    		$rv.config.clearStorage();
				ws.writeTextResponse(socketId, "", keepAlive, ws.CONTENT_TYPE_TEXT_PLAIN);
			} else if (cmd === "/restart") {
				log("restart command received");
				restart();
			} else if (cmd === "/reboot") {
				log("reboot command received");
				reboot();
	        } else if (cmd === "/shutdown") {
				log("shutdown command received");
				//ws.writeTextResponse(socketId, "", keepAlive, ws.CONTENT_TYPE_TEXT_PLAIN);
				shutdown();
	        } else if (cmd === "/config") {
	        	//var configHTML = rvGetConfigPageHtml($rv.config.displayId, $rv.config.claimId, $rv.config.server)
	        	var configHTML = configPage.get($rv.config.displayId, $rv.config.claimId, $rv.config.server, $rv.config.viewerServer);
				ws.writeTextResponse(socketId, configHTML, keepAlive, ws.CONTENT_TYPE_TEXT_HTML);
	        } else if (cmd === "/log") {
				ws.writeTextResponse(socketId, apilog, keepAlive, ws.CONTENT_TYPE_TEXT_HTML);
	        } else if (cmd === "/version") {
				ws.writeTextResponse(socketId, $rv.config.appVersion, keepAlive, ws.CONTENT_TYPE_TEXT_PLAIN);
	        } else {
				ws.writeErrorResponse(socketId, ws.HTTP_BAD_REQUEST_TEXT, keepAlive);
	        }
		} catch (e) {
    		log("onRequest error" + e.message);
			ws.writeErrorResponse(socketId, keepAlive, ws.HTTP_BAD_REQUEST_TEXT);
		}
        
	};

	var checkForUpdate = function() {
		log("checking for update...");
		chrome.runtime.requestUpdateCheck(function(status, details) {
			logToAPi("status=" + status);
			if (status == "update_found") {
				log("update pending... Version=" + (details ? details.version : "n/a"));
				//reboot is implemented in onUpdateAvailable in main.js
			} else if (status == "no_update") {
				log("no update found");
			} else if (status == "throttled") {
				log("asking too frequently");
			}
		});
	};

	var shutdown = function() {
		ws.stop();
		$rv.close();
	};

	var startViewerWithDelay = function() {
		setTimeout(startViewer, 3000);
	};

	var startViewer = function() {
		var viewerUrl = $rv.config.getViewerUrl();
		log("[start Viewer] URL: " + viewerUrl);
		$rv.browser.src = viewerUrl;
	};

	var restartViewer = function() {
		if (!$rv.debug) {
			var viewerUrl = $rv.config.getViewerUrl();
			log("[restart Viewer] URL: " + viewerUrl);
			if ($rv.browser.src === viewerUrl) {
				reloadViewer();
			} else {
				$rv.browser.src = viewerUrl;
			}
		}
	};

	var stopViewer = function() {
		log("[stop Viewer]");
		$rv.browser.stop();
	};
	
	var reloadViewer = function() {
		log("[reload Viewer]");
		$rv.browser.reload();
	};

	var restart = function() {
		//restart command needs to clear browser cache
		
		//do we need to navigate to other page first?
		var onPageLoad = function() {
			$rv.browser.removeEventListener('contentload', onPageLoad);
			//now we can clear cache
			//clearData is still in Dev channel, so using try-catch
			try {
				$rv.browser.clearData({since:0}, {
					appcache: true,
					cache: true,
					cookies: true,
					downloads: true,
					fileSystems: true,
					formData: true,
					history: true,
					indexedDB: true,
					localStorage: true,
					serverBoundCertificates: true,
					pluginData: true,
					passwords: true,
					webSQL: true}, function() {
						restartViewer();
					});
			} catch (e) {
				console.warn("cache clearing failed. Error: " + e.message);
				restartViewer();
			}
		};
		$rv.browser.addEventListener('contentload', onPageLoad);
		$rv.browser.src = "data:text/plain,reloading..." //or $rv.browser.src="about:blank"
	};
	
	var reboot = function() {
		chrome.runtime.getPlatformInfo(function(info) {
   			if (info.os == "cros") {
   				log("[reboot]");
   				chrome.runtime.reload();
   			} else {
				log("[reboot - not supported on "+info.os+", instead restarting viewer]");
				restartViewer();
	        	}	
		});
	};
	
	var startTimer_CheckForUpdate = function() {
		setTimeout(onTimer_CheckForUpdate,  60 * 60 * 1000); // 1 hour interval
	};

	var onTimer_CheckForUpdate = function() {
		startTimer_CheckForUpdate();
		checkForUpdate();
	};
	
	var setRestartTime = function(restartTimeAsText) {
		var timeDiffMS = parseRestartTime(restartTimeAsText);
		if (timeDiffMS > 0) {
			clearTimeout(restartTimer);
			restartTimer =	setTimeout(reboot, timeDiffMS);
		}
	};
	
	var parseRestartTime = function(restartTime) {
		//returns difference in ms between current time and restartTime
		var res = 0;
		try {
			var da = restartTime.split(":");
			if (da.length == 2) {
				var dt = new Date();
				dt.setHours(da[0])
				dt.setMinutes(da[1]);
				dt.setSeconds(0);
				//make sure this time has not passed yet
				res = dt.getTime() - new Date().getTime();
				if (res < 0) {
					res += 24*60*60*1000; // add 1 day
				}
			}
		} catch (e) {
		}
		return res;
	}

};

